/* C.Popen: Simulation of pipes */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"

#include "utils.h"

extern int _os_cmd (char *);

#define MAX_PIPE 20

typedef enum
{
	unopened = 0,
	reading,
	writing
}
pipemode;

static struct pipe
{
	char *command;	/* The command being executed		*/
	char *name;	/* The name of the pipe file		*/
	FILE *fd;	/* The file used as a pipe		*/
	pipemode pmode;	/* The open mode of the pipe		*/
	int retval;	/* The return value of the command	*/
}
pipes[MAX_PIPE];

FILE *popen (char *command, char *mode)
{
	FILE *current;
	char *name;
	int i;
	pipemode curmode;
	int rval = -1;
	char tmp[11];

	/* decide on mode */
	if ( mode[1] != 0 )
		return NULL;
	else if ( *mode == 'r' )
		curmode = reading;
	else if ( *mode == 'w' )
		curmode = writing;
	else
		return NULL;

	/* Get a slot in the pipes structure */
	for ( i = 0; i < MAX_PIPE; ++i )
	{
		if ( pipes[i].pmode == unopened )
			break;
	}

	if ( i >= MAX_PIPE )
		return NULL;

	/* Get a file name to use */
	sprintf(tmp, "Pipe%.2d", i);
	name = mktemp(tmp);

	if ( name == NULL )
		return NULL;

	/*
	 * If we're reading, just call system() to get a file filled
	 * with output.
	 */

	if ( curmode == reading )
	{
		char *tmpname;
		int oscmd = _os_cmd(command);
		char cmd[256];
		int n;

		if (*command == '%')
		{
			oscmd = 1;
			++command;
		}

		if (!oscmd)
		{
			char *s;

			while (*command && isspace(*command))
				++command;

			s = command;

			while (*s && !isspace(*s))
				++s;
			
			n = sprintf(cmd, "%.*s > %s%s",
				s - command, command, name, s);
		}
		else
		{
			tmpname = mktemp("PipeTmp");

			if (tmpname == NULL)
			{
				free(name);
				return NULL;
			}

			n = sprintf(cmd, "%s{ > %s }", command, tmpname);
		}

		/* Emergency! Overflow in command buffer! */
		if (n > 255)
		{
			if (oscmd)
			{
				remove(tmpname);
				free(tmpname);
			}
			free(name);
			return NULL;
		}

		_kernel_setenv("Sys$ReturnCode", "0");
		rval = system(cmd);

		if (rval == _kernel_ERROR)
		{
			remove(tmpname);
			free(tmpname);
			free(name);
			return NULL;
		}

		if (oscmd)
		{
			int ch;
			FILE *in = fopen(tmpname, "r");
			FILE *out = fopen(name, "w");

			if (in == NULL || out == NULL)
			{
				remove(tmpname);
				free(tmpname);
				free(name);
				return NULL;
			}

			/* Strip out CRs from the output */
			while ((ch = getc(in)) != EOF && !ferror(out))
			{
				if (ch != '\r')
					putc(ch, out);
			}

			/* Did we succeed? */
			ch = (ferror(in) || ferror(out));

			/* Tidy up */
			fclose(in);
			fclose(out);
			remove(tmpname);
			free(tmpname);

			if (ch)
			{
				free(name);
				return NULL;
			}
		}

		if ( (current = fopen(name,"r")) == NULL )
		{
			free(name);
			return NULL;
		}
	}
	else
	{
		if ( (current = fopen(name,"w")) == NULL )
		{
			free(name);
			return NULL;
		}
	}

	pipes[i].command = strdup(command);
	pipes[i].name = name;
	pipes[i].fd = current;
	pipes[i].pmode = curmode;
	pipes[i].retval = rval;
	return current;
}

int pclose (FILE *current)
{
	int rval;
	int i;

	/* Get the appropriate slot in thbe pipes structure */
	for ( i = 0; i < MAX_PIPE; ++i )
	{
		if ( pipes[i].fd == current )
			break;
	}

	if ( i >= MAX_PIPE )
		return -1;

	if ( pipes[i].pmode == reading )
	{
		/* Input pipes are just files we're done with */
		rval = pipes[i].retval;
		fclose(current);
		remove(pipes[i].name);
	}
	else
	{
		/*
		 * Output pipes are temporary files we have
		 * to cram down the throats of programs.
		 */
		char *command = pipes[i].command;
		int oscmd = _os_cmd(command);
		int n;
		char cmd[256];

		if (*command == '%')
		{
			oscmd = 1;
			++command;
		}

		/* Close the pipe file */
		fclose(current);

		/* Create the required command string */
		if (oscmd)
			n = sprintf(cmd, "%s{ < %s }", command, pipes[i].name);
		else
		{
			char *s;

			while (*command && isspace(*command))
				++command;

			s = command;

			while (*s && !isspace(*s))
				++s;
			
			n = sprintf(cmd, "%.*s < %s%s",
				s - command, command, pipes[i].name, s);
		}

		/* Check for overflow in command buffer */
		if (n > 255)
			rval = -1;
		else
		{
			_kernel_setenv("Sys$ReturnCode", "0");
			rval = system(cmd);
		}

		remove(pipes[i].name);
	}

	/* clean up current pipe */
	pipes[i].pmode = unopened;
	free(pipes[i].name);
	free(pipes[i].command);
	return rval;
}

#ifdef test
int main (int argc, char *argv[])
{
	FILE *fp;
	char *cmd;

	if (argc <= 1)
	{
		printf("Usage Popen [cmd or Popen ]cmd\n");
		return 0;
	}

	cmd = argv[1];

	if (*cmd++ == ']')
	{
		fp = popen(cmd,"w");
		fprintf(fp,"hello\nworld\nhow\nare\nyou\n");
		pclose(fp);
	}
	else
	{
		char buf[500];
		fp = popen(cmd,"r");
		while (!feof(fp))
		{
			if (!fgets(buf,499,fp))
			{
				printf("Read error!\n");
				return 1;
			}
			buf[strlen(buf)-1] = 0;
			printf(">%s<\n", buf);
		}
		pclose(fp);
	}

	return 0;
}
#endif
